﻿namespace Knownet.Utils
{
  using System;
  using System.IO;
  using System.Text;

  /// <summary>
  /// 标记可回溯字符串阅读器。
  /// </summary>
  public class MarkingStringReader:MarkingTextReader
  {
    #region Statics
    public static MarkingStringReader of(string text)
    {
      return new MarkingStringReader(text);
    }
    public static MarkingStringReader of(MarkingStringReader reader)
    {
      return new MarkingStringReader(reader);
    }
    public static MarkingStringReader of(string text, int currentIndex)
    {
      return new MarkingStringReader(text, currentIndex);
    }
    #endregion
    #region Fields
    /// <summary>
    /// 字符串阅读器中的文萡。
    /// </summary>
    protected string text;
    #endregion
    #region Properties
    /// <summary>
    /// 字符串阅读器中的文本。
    /// </summary>
    public virtual string Text
    {
      get
      {
        return this.text;
      }
    }
    /// <summary>
    /// 字符串阅读器中文本的长度。
    /// </summary>
    public virtual int TextLength
    {
      get
      {
        return (this.IsDisposed) ? -1 : this.Text.Length;
      }
    }
    public override long Position
    {
      get
      {
        return this.position;
      }
      set
      {
        this.position =(long)( (value >= 0) ? value : this.position);
      }
    }
    public override bool CanRead
    {
      get
      {
        return (!this.IsDisposed)? this.position < this.TextLength:false;
      }

    }
    public override bool IsDisposed
    {
      get
      {
        return this.Text == null;
      }
    }
    #endregion
    #region Constructors
    public MarkingStringReader(string text):this(text,0)
    {
    }
    public MarkingStringReader(string text, int currentIndex)
    {
      this.text = (text == null) ? string.Empty : text;
      this.position = (currentIndex < 0 || currentIndex >= text.Length) ? 0 : currentIndex;
    }
    public MarkingStringReader(MarkingStringReader reader)
    {
      if (reader == null || reader.IsDisposed )
      {
        this.text = string.Empty;
      }
      else
      {
        this.text = reader.Text;
        this.position = reader.position;
      }
    }
    #endregion
    #region Methods
    public override MarkingTextReader Duplicate()
    {
      return new MarkingStringReader(this);
    }
    public override int Peek()
    {
      if (this.IsDisposed) throw new ObjectDisposedException("the reader is disposed!");

      return this.position < this.TextLength - 1 ? this.Text[(int)this.position + 1] : -1;
    }
    public override int Read()
    {
      if (this.IsDisposed) throw new ObjectDisposedException("the reader is disposed!");

      return this.position < this.TextLength -1 ? this.Text[(int)(++this.position)] : -1;
    }
    public override int Read(char[] buffer, int index, int count)
    {

      if (buffer == null) throw new ArgumentNullException("buffer", "buffer can not be null!");
      if (index < 0) throw new ArgumentOutOfRangeException("index", "index is less than 0!");
      if (count < 0) throw new ArgumentOutOfRangeException("count", "count is less than 0!");
      if (buffer.Length - index < count) throw new ArgumentException("buffer length minus index is less than count!");
      if (this.IsDisposed) throw new ObjectDisposedException("the reader is disposed!");

      long contentLength = this.TextLength - this.position - 1;
      long readcount = contentLength >= count ? count : contentLength;

      int p = (int)this.position;
      if (readcount >= 0)
      {
        for (int i = 0; i < readcount; i++)
        {
          buffer[i] = this.Text[i + p];
        }
        this.position += readcount;
      }
      return (int)(readcount>0?readcount:0);
    }
    public override int ReadBlock(char[] buffer, int index, int count)
    {
      return this.Read(buffer, index, count);
    }
    public override string ReadText(ref int length)
    {
      if (length <= 0) throw new ArgumentException("length <=0 is not allowed!");
      int contentLength = this.TextLength - (int)this.position - 1;
      length = contentLength >= length ? length : contentLength;
      int pos = (int)this.position;
      this.position += length;
      return this.Text.Substring(pos, length);
    }
    public override string ReadLine()
    {
      if (this.IsDisposed) throw new ObjectDisposedException("the reader is disposed!");

      StringBuilder builder = new StringBuilder();

      while (this.CanRead)
      {
        char ch = this.Peekchar();
        if (ch == '\r') //'\r'
        {
          this.SkipChar();

          if (ch == '\n') //'\r\n'
          {
            this.SkipChar();
          }
          this.location.IncreaseRow();
          return builder.ToString();
        }
        else if (ch == '\n') //'\n'
        {
          this.SkipChar();
          this.location.IncreaseRow();
          return builder.ToString();
        }
        ch = this.Readchar();
        
        builder.Append(ch);

        this.location.IncreaseChar((ch=='\t')?this.tabCount:1);
      }
      return builder.ToString();
    }
    public override string ReadToEnd()
    {
      if (this.IsDisposed) throw new ObjectDisposedException("the reader is disposed!");
      string str = this.Text.Substring((int)this.position);
      this.position = this.TextLength;
      this.Relocate(str);
      return str;
    }
    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        this.position = 0;
        this.text = null;
      }
    }
    #endregion

  }
}
